﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Media.Imaging;

namespace Manas.Silverlight.DynamicDeepZoom
{
    public partial class DeepZoomControl : UserControl
    {
        #region Initialization

        public DeepZoomControl()
        {
            InitializeComponent();
            InitializeHandlers();
        }

        #endregion

        #region Constants

        const double InitialZoom = 0.1;
        const double WheelZoom = 1.2;
        const double ClickZoom = 1.2;
        const double MinZoom = 0.05;
        const double MaxZoom = 6;

        #endregion

        #region Viewport Handling

        double zoom = 1;
        bool duringDrag = false;
        bool mouseDown = false;
        Point lastMouseDownPos = new Point();
        Point lastMousePos = new Point();
        Point lastMouseViewPort = new Point();

        public double ZoomFactor
        {
            get { return zoom; }
            set { zoom = value; }
        }
        
        private void InitializeHandlers()
        {
            this.Loaded += new RoutedEventHandler(Page_Loaded);
            this.UiImage.SizeChanged += new SizeChangedEventHandler(UiImage_SizeChanged);

            //
            // Firing an event when the MultiScaleImage is Loaded
            //
            this.UiImage.Loaded += new RoutedEventHandler(UiImage_Loaded);

            //
            // Firing an event when all of the images have been Loaded
            //
            this.UiImage.ImageOpenSucceeded += new RoutedEventHandler(UiImage_ImageOpenSucceeded);

            //
            // Handling all of the mouse and keyboard functionality
            //
            this.MouseMove += delegate(object sender, MouseEventArgs e)
            {
                lastMousePos = e.GetPosition(UiImage);

                if (duringDrag)
                {
                    Point newPoint = lastMouseViewPort;
                    newPoint.X += (lastMouseDownPos.X - lastMousePos.X) / UiImage.ActualWidth * UiImage.ViewportWidth;
                    newPoint.Y += (lastMouseDownPos.Y - lastMousePos.Y) / UiImage.ActualWidth * UiImage.ViewportWidth;
                    UiImage.ViewportOrigin = newPoint;
                }
            };

            this.MouseLeftButtonDown += delegate(object sender, MouseButtonEventArgs e)
            {
                lastMouseDownPos = e.GetPosition(UiImage);
                lastMouseViewPort = UiImage.ViewportOrigin;

                mouseDown = true;

                UiImage.CaptureMouse();
            };

            this.MouseLeftButtonUp += delegate(object sender, MouseButtonEventArgs e)
            {
                if (!duringDrag)
                {
                    bool shiftDown = (Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift;
                    double newzoom = ZoomFactor;

                    if (shiftDown)
                    {
                        newzoom /= 2;
                    }
                    else
                    {
                        newzoom *= 2;
                    }

                    Zoom(newzoom, UiImage.ElementToLogicalPoint(this.lastMousePos));
                }
                duringDrag = false;
                mouseDown = false;

                UiImage.ReleaseMouseCapture();
            };

            this.MouseMove += delegate(object sender, MouseEventArgs e)
            {
                lastMousePos = e.GetPosition(UiImage);
                if (mouseDown && !duringDrag)
                {
                    duringDrag = true;
                    double w = UiImage.ViewportWidth;
                    Point o = new Point(UiImage.ViewportOrigin.X, UiImage.ViewportOrigin.Y);
                    UiImage.UseSprings = false;
                    UiImage.ViewportOrigin = new Point(o.X, o.Y);
                    UiImage.ViewportWidth = w;
                    ZoomFactor = 1/w;
                    UiImage.UseSprings = true;
                }

                if (duringDrag)
                {
                    Point newPoint = lastMouseViewPort;
                    newPoint.X += (lastMouseDownPos.X - lastMousePos.X) / UiImage.ActualWidth * UiImage.ViewportWidth;
                    newPoint.Y += (lastMouseDownPos.Y - lastMousePos.Y) / UiImage.ActualWidth * UiImage.ViewportWidth;
                    UiImage.ViewportOrigin = newPoint;
                }
            };


            this.MouseWheel += delegate(object sender, MouseWheelEventArgs e)
            {
                e.Handled = true;

                double newzoom = ZoomFactor;

                if (e.Delta < 0)
                    newzoom /= WheelZoom;
                else
                    newzoom *= WheelZoom;

                Zoom(newzoom, UiImage.ElementToLogicalPoint(this.lastMousePos));
                UiImage.CaptureMouse();
            };
        }

        void UiImage_SizeChanged(object sender, SizeChangedEventArgs e)
        {
        }

        void Page_Loaded(object sender, RoutedEventArgs e)
        {
            this.Focus();
        }

        void UiImage_ImageOpenSucceeded(object sender, RoutedEventArgs e)
        {
            UiImage.ViewportOrigin = new Point(0, 0);
            UiImage.ViewportWidth = 1 / InitialZoom;
            ZoomFactor = InitialZoom;
        }

        void UiImage_Loaded(object sender, RoutedEventArgs e)
        {

        }

        private void Zoom(double newzoom, Point p)
        {
            if (newzoom < MinZoom)
            {
                newzoom = MinZoom;
            }
            else if (newzoom > MaxZoom)
            {
                newzoom = MaxZoom;
            }

            UiImage.ZoomAboutLogicalPoint(newzoom / ZoomFactor, p.X, p.Y);
            ZoomFactor = newzoom;
        }

        private void ZoomInClick(object sender, System.Windows.RoutedEventArgs e)
        {
            Zoom(ZoomFactor * ClickZoom, UiImage.ElementToLogicalPoint(new Point(.5 * UiImage.ActualWidth, .5 * UiImage.ActualHeight)));
        }

        private void ZoomOutClick(object sender, System.Windows.RoutedEventArgs e)
        {
            Zoom(ZoomFactor / ClickZoom, UiImage.ElementToLogicalPoint(new Point(.5 * UiImage.ActualWidth, .5 * UiImage.ActualHeight)));
        }

        private void GoHomeClick(object sender, System.Windows.RoutedEventArgs e)
        {
        	this.UiImage.ViewportWidth = 1;
			this.UiImage.ViewportOrigin = new Point(0,0);
            ZoomFactor = InitialZoom;
        }

        private void GoFullScreenClick(object sender, System.Windows.RoutedEventArgs e)
        {
            GoFullScreen(); 
        }

        private void GoFullScreen()
        {
            if (!Application.Current.Host.Content.IsFullScreen)
            {
                Application.Current.Host.Content.IsFullScreen = true;
            }
            else
            {
                Application.Current.Host.Content.IsFullScreen = false;
            }
        }

        // Handling the VSM states
        private void LeaveMovie(object sender, System.Windows.Input.MouseEventArgs e)
        {
            VisualStateManager.GoToState(this, "FadeOut", true);
        }

        private void EnterMovie(object sender, System.Windows.Input.MouseEventArgs e)
        {
            VisualStateManager.GoToState(this, "FadeIn", true);
        }


        // Unused functions that show the inner math of Deep Zoom
        public Rect GetImageRect()
        {
            return new Rect(-UiImage.ViewportOrigin.X / UiImage.ViewportWidth, -UiImage.ViewportOrigin.Y / UiImage.ViewportWidth, 1 / UiImage.ViewportWidth, 1 / UiImage.ViewportWidth * UiImage.AspectRatio);
        }

        public Rect ZoomAboutPoint(Rect img, double zAmount, Point pt)
        {
            return new Rect(pt.X + (img.X - pt.X) / zAmount, pt.Y + (img.Y - pt.Y) / zAmount, img.Width / zAmount, img.Height / zAmount);
        }

        public void LayoutDZI(Rect rect)
        {
            double ar = UiImage.AspectRatio;
            UiImage.ViewportWidth = 1 / rect.Width;
            UiImage.ViewportOrigin = new Point(-rect.Left / rect.Width, -rect.Top / rect.Width);
        }

        #endregion
    }

}
